//=========================================================================
// Copyright (C) 2024 The C++ Component Model(COMO) Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//=========================================================================

#ifndef __VOTER_LOGGER_H__
#define __VOTER_LOGGER_H__

#include <stdbool.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C"
{
#endif

// 2 的N次幂
#define VOTER_LOGGERS_SIZE  (2 << 16)

typedef struct tagVoterRunningState {
    int64_t crcErrorMemoryOrBus;
    int64_t crcErrorTcp;
    int64_t crcErrorUdp;
    int64_t crcErrorZmq;
    int64_t notInWhiteListIP;
    int64_t votingResultFail;
    int64_t countExcessiveTimingError;
    int64_t maxExcessiveTimingErrorNs;
} VoterRunningState;
extern VoterRunningState *g_pVoterRunningState;

int32_t WriteLog(char *buf, int32_t dataLen);
char *ReadLog(char *buf, int32_t bufSize);
int InitLog(void);

/**
 * 下面的函数在表决器程序中不要直接引用，要保持 Log 数据的完整性，每条数据都是存成:
 *     长度 + 内容
 * 的形式，其中 长度 是 int32_t 类型的，内容的长度是以字节为单位。
 */

/**
 * Checks if the buffer_size is a power of two.
 * Due to the design only <tt> RING_BUFFER_SIZE-1 </tt> items
 * can be contained in the buffer.
 * buffer_size must be a power of two.
*/
#define RING_BUFFER_IS_POWER_OF_TWO(buffer_size) \
                                        ((buffer_size & (buffer_size - 1)) == 0)

/**
 * Used as a modulo operator
 * as <tt> a % b = (a & (b − 1)) </tt>
 * where \c a is a positive index in the buffer and
 * \c b is the (power of two) size of the buffer.
 */
#define RING_BUFFER_MASK(rb) (rb->buffer_mask)

/**
 * Structure which holds a ring buffer.
 * The buffer contains a buffer array
 * as well as metadata for the ring buffer.
 */
typedef struct tagVoterLogger {
    char *buffer;          // Buffer memory.
    int   buffer_mask;     // Buffer mask.
    int   tail_index;      // Index of tail.
    int   head_index;      // Index of head.
} VoterLogger;

/**
 * Initializes the ring buffer pointed to by <em>buffer</em>.
 * This function can also be used to empty/reset the buffer.
 * The resulting buffer can contain <em>buf_size-1</em> bytes.
 * @param buffer The ring buffer to initialize.
 * @param buf The buffer allocated for the ringbuffer.
 * @param buf_size The size of the allocated ringbuffer.
 */
int ring_buffer_init(VoterLogger *vloger, char *buf, int buf_size);

/**
 * Adds a byte to a ring buffer.
 * @param buffer The buffer in which the data should be placed.
 * @param data The byte to place.
 */
void ring_buffer_queue(VoterLogger *vloger, char data);

/**
 * Adds an array of bytes to a ring buffer.
 * @param buffer The buffer in which the data should be placed.
 * @param data A pointer to the array of bytes to place in the queue.
 * @param size The size of the array.
 */
void ring_buffer_queue_arr(VoterLogger *vloger, const char *data, int size);

/**
 * Returns the oldest byte in a ring buffer.
 * @param buffer The buffer from which the data should be returned.
 * @param data A pointer to the location at which the data should be placed.
 * @return 1 if data was returned; 0 otherwise.
 */
int ring_buffer_dequeue(VoterLogger *vloger, char *data);

/**
 * Returns the <em>len</em> oldest bytes in a ring buffer.
 * @param buffer The buffer from which the data should be returned.
 * @param data A pointer to the array at which the data should be placed.
 * @param len The maximum number of bytes to return.
 * @return The number of bytes returned.
 */
int ring_buffer_dequeue_arr(VoterLogger *vloger, char *data, int len);
/**
 * Peeks a ring buffer, i.e. returns an element without removing it.
 * @param buffer The buffer from which the data should be returned.
 * @param data A pointer to the location at which the data should be placed.
 * @param index The index to peek.
 * @return 1 if data was returned; 0 otherwise.
 */
int ring_buffer_peek(VoterLogger *vloger, char *data, int index);

/**
 * Returns whether a ring buffer is empty.
 * @param buffer The buffer for which it should be returned whether it is empty.
 * @return 1 if empty; 0 otherwise.
 */
inline bool ring_buffer_is_empty(VoterLogger *vloger) {
    return (vloger->head_index == vloger->tail_index);
}

/**
 * Returns whether a ring buffer is full.
 * @param buffer The buffer for which it should be returned whether it is full.
 * @return 1 if full; 0 otherwise.
 */
inline bool ring_buffer_is_full(VoterLogger *vloger) {
    return ((vloger->head_index - vloger->tail_index) &
                          RING_BUFFER_MASK(vloger)) == RING_BUFFER_MASK(vloger);
}

/**
 * Returns the number of items in a ring buffer.
 * @param buffer The buffer for which the number of items should be returned.
 * @return The number of items in the ring buffer.
 */
inline int ring_buffer_num_items(VoterLogger *vloger) {
    return ((vloger->head_index - vloger->tail_index) & RING_BUFFER_MASK(vloger));
}

#ifdef __cplusplus
}
#endif

#endif // __VOTER_LOGGER_H__
